package com.nuoniu.sibanyun.service.production.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.common.lang.StringUtils;
import com.nuoniu.sibanyun.entity.dto.MrpResultDto;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.erp.in.InInventory;
import com.nuoniu.sibanyun.entity.production.ErpBomDetail;
import com.nuoniu.sibanyun.entity.production.ErpMrpProgramme;
import com.nuoniu.sibanyun.entity.production.ErpMrpRecord;
import com.nuoniu.sibanyun.entity.production.ErpMrpResult;
import com.nuoniu.sibanyun.mapper.production.ErpMrpRecordMapper;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.erp.in.IInInventoryInfoService;
import com.nuoniu.sibanyun.service.erp.in.IInInventoryService;
import com.nuoniu.sibanyun.service.production.IErpMrpRecordService;
import com.nuoniu.sibanyun.service.production.IErpMrpResultService;
import com.nuoniu.sibanyun.service.production.IVBomDetailService;
import com.nuoniu.sibanyun.service.so.IVSaleOrderMainService;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * mrp运算记录 服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2021-03-16
 */
@Service
public class ErpMrpRecordServiceImpl extends ServiceImpl<ErpMrpRecordMapper, ErpMrpRecord> implements IErpMrpRecordService {

    @Autowired
    private IVBomDetailService bomDetailService;
    @Autowired
    private IInInventoryInfoService inventoryInfoService;
    @Autowired
    private IInInventoryService inventoryService;
    @Autowired
    private IErpAccountService accountService;
    @Autowired
    private IVSaleOrderMainService saleOrderMainService;
    @Autowired
    private IErpMrpResultService mrpResultService;
    @Autowired
    private IErpNumberSetService numberSetService;

    @Override
    @Transactional
    public List<ErpMrpResult> mrpOperation(List<ErpMrpRecord> mrpData, ErpMrpProgramme mrpProgramme) {
        UserInfo userInfo = UserThreadLocal.get();
        Map<Integer, BigDecimal> inventoryNumberMap = new HashMap(); //物料建议总量
        List<ErpMrpResult> erpMrpResults = new ArrayList<>(); //运算结果(不合并)

        mrpData.forEach(m -> {
            if(m.getOperationQuantity() == null || m.getOperationQuantity().compareTo(BigDecimal.ZERO) == -1) {
                throw new NuoNiuException("运算数量必须大于0");
            }

            if(m.getInventoryId() == null) {
                throw new NuoNiuException("请选择参与的物料档案");
            }

            InInventory inventory = inventoryService.getById(m.getInventoryId());
            if(inventory == null) {
                return;
            }

            if((inventory.getIsPurchasing() == null || !inventory.getIsPurchasing()) && (inventory.getIsMadeSelf() == null || !inventory.getIsMadeSelf())
                    && (inventory.getIsMaterial() == null || !inventory.getIsMaterial())) {
                System.out.println("物料档案" + inventory.getInventoryName() + "  没有对应属性，不参与运算");
                return;
            }

            m.setCode(getCode());
            BigDecimal inCount = new BigDecimal(0.0);
            if(inventoryNumberMap.get(m.getInventoryId()) == null) {
                if(mrpProgramme.getIsSafeInventory() != null && mrpProgramme.getIsSafeInventory() && inventory.getSafeStorage() != null) { //判断是否考虑安全库存
                    inCount = inCount.subtract(inventory.getSafeStorage());
                }

                if(mrpProgramme.getIsExistInventory() != null && mrpProgramme.getIsExistInventory()) { //判断是否考虑库存
                    Double count = inventoryInfoService.getInventoryCount(m.getInventoryId(), mrpProgramme);
                    if(count != null) {
                        inCount = inCount.add(new BigDecimal(count)).setScale(2, BigDecimal.ROUND_HALF_UP);
                    }
                }

                if(mrpProgramme.getIsWarehouseDistributionQuantity() != null && mrpProgramme.getIsWarehouseDistributionQuantity()) { //是否考虑预计入库数量和分配数量
                    String warehouseIds = mrpProgramme.getWarehouseIds();
                    Set<Integer> warehouseSet = new HashSet<>();
                    if(StringUtils.isNotBlank(warehouseIds)){
                        String[] split = warehouseIds.split(",");
                        Set<String> warehouseStrSet = new HashSet<>(Arrays.asList(split));
                        warehouseStrSet.forEach(s-> warehouseSet.add(Integer.valueOf(s)));
                    }
                    Map<Integer, BigDecimal> availableQuantityMap = inventoryService.getAvailableQuantityMap(mrpData.stream().map(ErpMrpRecord::getInventoryId).collect(Collectors.toSet()),warehouseSet);
                    inCount = inCount.add(availableQuantityMap.get(inventory.getId().intValue()));
                }
            }

            if(m.getOperationQuantity().compareTo(inCount) == 1) { //对比现有库存
                ErpMrpResult result = new ErpMrpResult();
                result.setCompanyId(userInfo.getCompanyId());
                result.setPurchasingLeadTime(inventory.getPurchasingLeadTime());
                result.setAccountId(accountService.getCurrentAccountId());
                result.setInventoryId(m.getInventoryId());
                result.setInventoryCode(m.getInventoryCode());
                result.setInventoryName(m.getInventoryName());
                result.setInventorySpecification(m.getInventorySpecification());
                result.setVoucherCode(m.getVoucherCode());
                result.setVoucherId(m.getOrderId());
                result.setVoucherType(m.getVoucherType());
                result.setBomId(m.getBomId());
                result.setMrpCode(getCode());
                if(inventory.getUnitType().equals("多计量")) {
                    result.setUnitId(inventory.getMainUnit());
                    result.setUnitName(inventory.getMainUnitName());
                    result.setUnitGroupId(inventory.getUnitGroupId());
                }else {
                    result.setUnitId(inventory.getUnitId());
                    result.setUnitName(inventory.getUnitName());
                }
                result.setBomVersionCode(m.getBomVersionCode());
                result.setNeedDate(m.getNeedDate());
                result.setPlanBeginDate(m.getPlanBeginDate());
                result.setPlanEndDate(m.getPlanEndDate());
                result.setSuggestAmount(m.getOperationQuantity().subtract(inCount));
                if(inventoryNumberMap.get(m.getInventoryId()) != null) {
                    inventoryNumberMap.put(m.getInventoryId(), result.getSuggestAmount().add(inventoryNumberMap.get(m.getInventoryId())));
                }else {
                    inventoryNumberMap.put(m.getInventoryId(), result.getSuggestAmount());
                }

                if(inventory.getIsPurchasing()) { //根据物料属性生成建议单
                    result.setType(1);
                }else {
                    result.setType(2);
                }

                erpMrpResults.add(result);
                if(m.getBomId() != null) { //运算BOM
                    mrpOperationBom(m.getBomId(), erpMrpResults, inventoryNumberMap, mrpProgramme, result.getSuggestAmount());
                }
            }
        });

        //异步修改单据运算状态
//        saleOrderMainService.updateOperationStatus(mrpData);
        //保存运算记录
        this.saveBatch(mrpData);
        //处理数据结果
        if(mrpProgramme.getMergeType() != null && !mrpProgramme.getMergeType().equals(1)) {
            List<ErpMrpResult> mrpResults = new ArrayList<>(); //运算结果(合并)
            Map<Integer, Object> resultMap = new HashMap<>();
            erpMrpResults.forEach(r -> {
                if(resultMap.get(r.getInventoryId()) == null) {
                    resultMap.put(r.getInventoryId(), r);
                    r.setSuggestAmount(inventoryNumberMap.get(r.getInventoryId()));
                    mrpResults.add(r);
                }
            });
            //保存运算结果(合并)
            mrpResultService.saveBatch(mrpResults);
            return mrpResults;
        }
        //保存运算结果(不合并)
        mrpResultService.saveBatch(erpMrpResults);
        return erpMrpResults;
    }

    public void mrpOperationBom(Integer bomId, List<ErpMrpResult> erpMrpResults, Map<Integer, BigDecimal> inventoryNumberMap, ErpMrpProgramme mrpProgramme, BigDecimal surplusCount) {
        List<ErpBomDetail> bomDetails = bomDetailService.getByBomId(bomId.longValue());
        bomDetails.forEach(b -> {
            InInventory inventory = inventoryService.getById(b.getInventoryId());
            if(inventory == null) {
                return;
            }

            if((inventory.getIsPurchasing() == null || !inventory.getIsPurchasing()) && (inventory.getIsMadeSelf() == null || !inventory.getIsMadeSelf())
                    && (inventory.getIsMaterial() || !inventory.getIsMaterial())) {
                System.out.println("物料档案" + inventory.getInventoryName() + "  没有对应属性，不参与运算");
                return;
            }

            BigDecimal inCount = new BigDecimal(0.0);
            if(inventoryNumberMap.get(b.getInventoryId()) == null) {
                if(mrpProgramme.getIsExistInventory() != null && mrpProgramme.getIsExistInventory()) { //判断是否考虑库存
                    Double count = inventoryInfoService.getInventoryCount(b.getInventoryId(), mrpProgramme);
                    if(count != null) {
                        inCount = inCount.add(new BigDecimal(count));
                    }
                }

                if(mrpProgramme.getIsSafeInventory() != null && mrpProgramme.getIsSafeInventory() && inventory.getSafeStorage() != null) { //判断是否考虑安全库存
                    inCount = inCount.subtract(inventory.getSafeStorage());
                }

                if(mrpProgramme.getIsWarehouseDistributionQuantity() != null && mrpProgramme.getIsWarehouseDistributionQuantity()) { //是否考虑预计入库数量和分配数量
                    Set<Integer> warehouseSet = new HashSet<>();
                    String warehouseIds = mrpProgramme.getWarehouseIds();
                    if(StringUtils.isNotBlank(warehouseIds)){
                        String[] split = warehouseIds.split(",");
                        Set<String> warehouseStrSet = new HashSet<>(Arrays.asList(split));
                        warehouseStrSet.forEach(s-> warehouseSet.add(Integer.valueOf(s)));
                    }
                    Map<Integer, BigDecimal> bomAvailableQuantityMap = inventoryService.getAvailableQuantityMap(bomDetails.stream().map(ErpBomDetail::getInventoryId).collect(Collectors.toSet()),warehouseSet);
                    inCount = inCount.add(bomAvailableQuantityMap.get(inventory.getId().intValue()));
                }
                //inventoryNumberMap.put(b.getInventoryId(), inCount);
            }

            BigDecimal needCount = surplusCount.multiply(b.getBaseQuantity());
            if(mrpProgramme.getLossRate()) { //是否考虑损耗率
                needCount = needCount.divide(new BigDecimal(1.0).subtract((new BigDecimal(b.getLossRate()).divide(new BigDecimal(100.0), 2, BigDecimal.ROUND_HALF_UP))), 2, BigDecimal.ROUND_HALF_UP );
            }

            if(needCount.compareTo(inCount) == 1) { //对比库存
                ErpMrpResult result = new ErpMrpResult();
                result.setPurchasingLeadTime(inventory.getPurchasingLeadTime());
                result.setCompanyId(erpMrpResults.get(0).getCompanyId());
                result.setAccountId(accountService.getCurrentAccountId());
                result.setInventoryId(b.getInventoryId());
                result.setInventoryName(b.getInventoryName());
                result.setInventoryCode(b.getInventoryCode());
                result.setVoucherCode(erpMrpResults.get(0).getVoucherCode());
                result.setVoucherId(erpMrpResults.get(0).getVoucherId());
                result.setVoucherType(erpMrpResults.get(0).getVoucherType());
                if(inventory.getUnitType().equals("多计量")) {
                    result.setUnitName(inventory.getMainUnitName());
                    result.setUnitId(inventory.getMainUnit());
                }else {
                    result.setUnitId(inventory.getUnitId());
                    result.setUnitName(inventory.getUnitName());
                }
                result.setInventorySpecification(b.getSpecification());
                result.setBomId(b.getBomId());
                result.setBomVersionCode(b.getBomVersion());
                result.setMrpCode(getCode());
                result.setNeedDate(erpMrpResults.get(0).getNeedDate());
                result.setPlanBeginDate(erpMrpResults.get(0).getPlanBeginDate());
                result.setPlanEndDate(erpMrpResults.get(0).getPlanEndDate());
                result.setSuggestAmount(needCount.subtract(inCount));
                if(inventoryNumberMap.get(b.getInventoryId()) != null) {
                    inventoryNumberMap.put(b.getInventoryId(), result.getSuggestAmount().add(inventoryNumberMap.get(b.getInventoryId())));
                }else {
                    inventoryNumberMap.put(b.getInventoryId(), result.getSuggestAmount());
                }

                if(inventory.getIsPurchasing()) {
                    result.setType(1);
                }else {
                    result.setType(2);
                }

                erpMrpResults.add(result);
                if(b.getBomId() != null) {
                    if(b.getBomId() != null) {
                        mrpOperationBom(b.getBomId(), erpMrpResults, inventoryNumberMap, mrpProgramme, result.getSuggestAmount());
                    }
                }
            }
        });
    }

    @Override
    public String getCode() {
        UserInfo userInfo = UserThreadLocal.get();
        String dateStr = "%Y-%m-%d";
        String code = baseMapper.getNewestCode(userInfo.getCompanyId(), dateStr);
        ErpNumberSet numberSet = new ErpNumberSet();
        numberSet.setMedian(1);
        numberSet.setStreamNumber(4);
        numberSet.setIsSplitMark(false);
        numberSet.setCurrentCode("MRP");
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    public void saveResult(MrpResultDto mrpResult) {
        mrpResultService.saveBatch(mrpResult.getMrpResults());
        saleOrderMainService.updateOperationStatus(mrpResult.getMrpRecords());
    }
}
